package cn.kgc.websocket;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 自定义WebSocket处理器，处理WebSocket连接和消息
 *
 * @author YC
 */
@Slf4j
public class WebSocketServerHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    /**
     * 存储所有活跃的WebSocket连接
     */
    private static final ConcurrentMap<String, ChannelHandlerContext> ACTIVE_CHANNELS = new ConcurrentHashMap<>();

    /**
     * 日期时间格式化器
     */
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 处理接收到的WebSocket消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        // 处理接收到的WebSocket消息
        if (frame instanceof TextWebSocketFrame) {
            // 处理文本消息
            String text = ((TextWebSocketFrame) frame).text();
            String channelId = ctx.channel().id().asShortText();
            log.info("收到来自 {} 的消息: {}", channelId, text);
            // 响应消息
            String response = String.format("[%s] 来自 %s 的消息: %s", LocalDateTime.now().format(FORMATTER), channelId, text);
            // 广播消息给所有在线客户端（除了发送者）
            broadcast(response, ctx);
            // 也可以只回复给发送者
//            ctx.writeAndFlush(new TextWebSocketFrame("服务器已收到: " + text));
        } else {
            // 不支持的消息类型
            String message = "不支持的消息类型: " + frame.getClass().getName();
            log.warn(message);
            ctx.writeAndFlush(new TextWebSocketFrame(message));
        }
    }

    /**
     * 新增连接
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        super.handlerAdded(ctx);
    }

    /**
     * 移除连接
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        super.handlerRemoved(ctx);
    }

    /**
     * 客户端连接成功时触发
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 客户端连接成功时触发
        String channelId = ctx.channel().id().asShortText();
        ACTIVE_CHANNELS.put(channelId, ctx);
        log.info("新的WebSocket连接: {}", channelId);
        // 向客户端发送连接成功消息
        String welcomeMsg = String.format("[%s] 连接成功，当前在线人数: %d", LocalDateTime.now().format(FORMATTER), ACTIVE_CHANNELS.size());
//        ctx.writeAndFlush(new TextWebSocketFrame(welcomeMsg));
        broadcast(welcomeMsg, ctx);
    }

    /**
     * 客户端断开连接时触发
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 客户端断开连接时触发
        String channelId = ctx.channel().id().asShortText();
        ACTIVE_CHANNELS.remove(channelId);
        log.info("WebSocket连接断开: {}", channelId);
        // 通知其他在线客户端
        String leaveMsg = String.format("[%s] 有客户端断开连接，当前在线人数: %d", LocalDateTime.now().format(FORMATTER), ACTIVE_CHANNELS.size());
        broadcast(leaveMsg, ctx);
    }

    /**
     * 处理WebSocket握手完成事件
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            WebSocketServerProtocolHandler.HandshakeComplete handshake = (WebSocketServerProtocolHandler.HandshakeComplete) evt;
            log.info("WebSocket握手完成，请求URI: {}", handshake.requestUri());
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * 处理异常
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("WebSocket处理异常", cause);
        // 异常时关闭通道
        ctx.close();
    }

    /**
     * 广播消息给所有在线客户端（可选排除发送者）
     *
     * @param message    要广播的消息
     * @param excludeCtx 要排除的客户端上下文（可以为null）
     */
    private void broadcast(String message, ChannelHandlerContext excludeCtx) {
        TextWebSocketFrame frame = new TextWebSocketFrame(message);
        for (ChannelHandlerContext ctx : ACTIVE_CHANNELS.values()) {
//            if (excludeCtx == null || !ctx.equals(excludeCtx)) {
            ctx.writeAndFlush(frame.retain());
//            }
        }
    }

}
